spring MVC处理Excel视图的三种方式
前段时间在一个项目里面发现,针对Excel的处理没有一个公用的视图,来个下载的需求就要自己去写一堆POI的东西,终于有一天给我也来了几个,还是按照以前的方式来写,写多了真心想吐,后面想想还是有必要整个解析Excel的视图了。花了一天时间,总结出来共有三种方式可以处理Excel视图。
由于spring已经提供了excel的抽象视图,所以我们直接继承过来就可以了。由于POI对excel2007和2003处理方式有些不同,所以spring4.2以上版本提供了两个excel抽象视图AbstractXlsxView(2007)、AbstractXlsView(2003)。现在又想到曾经公司的报表系统了,下载一年的销售数据,有上百万的数据在一个excel里面,打开直接卡死,后面还得分批次查询去搞,针对这种情况,程序里面其实可以一次性搞定的,分页查询、切割结果集到多个列表、多线程处理等等,所以处理的结果集最后还是放到Map里面的,以防数据量大的时候分成多个列表下载。这里还是只分析3中视图如何处理的,关于性能方面的东西,在实际项目中还是要充分考虑,这里就不分析了。下面的代码都是以excel2003进行分析。
这三种解析方式都要用到具体的Excel实现类,代码如下:
public class ExcelView extends AbstractXlsView {
@Override
protected void buildExcelDocument(Map<String, Object> model, Workbook workbook, HttpServletRequest request,
HttpServletResponse response) throws Exception {
response.setHeader("Content-Disposition", "attachment;filename="+ new String((DateFormatUtils.format(new Date(), "yyyyMMddHHmmss") + ".xls").getBytes(), "iso-8859-1"));
//分割list
for (Entry<String, Object> e : model.entrySet()) {
HSSFSheet sheet = (HSSFSheet) workbook.createSheet("测试");
if (e.getValue() instanceof List) {
List<?> dataList = (List<?>) e.getValue();
HSSFRow rowHeader = sheet.createRow(0);
//添加header
rowHeader.createCell(0).setCellValue("id");
rowHeader.createCell(1).setCellValue("名字");
for (int start = 0; start < dataList.size(); start++) {
HSSFRow row = sheet.createRow(start + 1);
String[] rowsText = dataList.get(start).toString().split(",");
for (int col = 0; col < rowsText.length; col++) {
HSSFCell cell = row.createCell(col);
cell.setCellValue(rowsText[col]);
}
}
}
}
}
}
由于在AbstractXlsView里面,已经做了ContentType以及流的写入,所以我们直接把HSSFSheet里面的数据设置下就可以了。
直接添加excel视图到ModelAndView
这种方式和其他方式差不多,只是把ExcelView作为参数传到ModelAndView,代码如下:
@RequestMapping("/download")
public ModelAndView test() {
ModelAndView mav = new ModelAndView();
ExcelView excelView = new ExcelView();
List<Student> list = new ArrayList<>();
Student student = new Student();
student.setId(1);
student.setName("hello");
list.add(student);
student = new Student();
student.setId(2);
student.setName("world");
list.add(student);
mav.addObject("list", list);
mav.setView(excelView);
return mav;
}
不用做其他配置,发送一个请求就可以得到excel文件了,结果如下:
自定义视图解析器
类似于解析jsp,只要给定相应规则把请求指定到配置的解析器就可以了,代码如下:
public class ExcelViewResolver extends AbstractCachingViewResolver implements Ordered {
private ApplicationContext context;
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
@Override
public boolean isCache() {
return false;
}
/**
* 直接在容器中配置视图,单例获取bean,不用每次新建,待优化 TODO
*/
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
context = super.getApplicationContext();
if (context != null) {
return context.getBean(viewName, View.class);
}
return null;
}
}
配置文件:
<bean class="com.myspring.web.view.ExcelViewResolver">
<property name="order" value="0"/>
</bean>
<bean id="excelView" class="com.myspring.web.view.ExcelView"/>
这种方式需要指定视图的名字为excelView,controller代码如下:
@RequestMapping("/download1")
public ModelAndView excel1() {
//针对指定的视图解析
ModelAndView mv = new ModelAndView("excelView");
List<Student> list = new ArrayList<>();
Student student = new Student();
student.setId(1);
student.setName("你好");
list.add(student);
student = new Student();
student.setId(2);
student.setName("世界");
list.add(student);
mv.addObject("list", list);
return mv;
}
运行结果:
自定义注解解析返回值
spring解析json视图的时候只要@ResponseBody注解就可以了,也不用其他配置,非常方便。解析excel一样可以这样做。首先自定义一个注解:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Excel {
}
实现一个自定义的返回值处理器:
public class ExcelMethodProcessor implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotationUtils.findAnnotation(returnType.getContainingClass(), Excel.class) != null ||
returnType.getMethodAnnotation(Excel.class) != null);
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
mavContainer.setRequestHandled(true);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
ExcelView view = new ExcelView();
if (returnValue instanceof Map) {
view.render( (Map)returnValue, request, response);
} else {
ModelMap model = new ModelMap();
model.addAttribute("returnValue", returnValue);
view.render(model, request, response);
}
}
}
配置文件里面需要把这个bean注入到容器:
<mvc:annotation-driven>
<mvc:return-value-handlers>
<bean class="com.myspring.web.view.ExcelMethodProcessor"/>
</mvc:return-value-handlers>
</mvc:annotation-driven>
配置完之后,我们就只需在Controller层加个注解就完事儿了,非常简洁,代码如下:
@RequestMapping("/download2")
@Excel
public List<Student> excel2() {
List<Student> list = new ArrayList<>();
Student student = new Student();
student.setId(1);
student.setName("Tom");
list.add(student);
student = new Student();
student.setId(2);
student.setName("Jerry");
list.add(student);
return list;
}
运行结果: